package org.curl;

/**
 * The curl class is a JNI wrapper for libcurl. Please bear with me, I'm no
 * true java dude (yet). Improve what you think is bad and send me the
 * updates!
 * This is meant as a raw, crude and low-level interface to libcurl. If you
 * want fancy stuff, build upon this.<br>
 * 
 * @author Daniel daniel@haxx.se
 */
public class CurlGlue {

	private int javacurl_handle;

	/* constructor and destructor for the libcurl handle */
	private native int jni_init();

	/* called by finalize() */
	private native void jni_cleanup(int javacurl_handle);

	/* called by perform() */
	private native synchronized int jni_perform(int javacurl_handle);
	
	/* called by formadd() */
	private native int jni_formadd(int libcurl, 
			int form_opt_copyname, String name,
			int form_opt_file, String file,
			int form_opt_type, String type);
	private native int jni_formfree(int libcurl);

	// Instead of varargs, we have different functions for each
	// kind of type setopt() can take

	// set a curl option string value
	private native int jni_setopt(int libcurl, int option, String value);
	// set a curl option Long value
	private native int jni_setopt(int libcurl, int option, int value);
	// set a curl option callback function
	// * only CURL.OPT_WRITEFUNCTION is implemented
	private native int jni_setopt(int libcurl, int option, CurlWrite value);


	/**
	 * Public constructor. Initializes the libcurl interface and
	 * creates a libcurl handle for each class instance. The handles
	 * need to be released by calling <code>finalize()</code><br>
	 * @see #finalize() finalize()
	 *
	 */
	public CurlGlue() {
		try {
			if ((javacurl_handle = jni_init()) == 0)
				error(CURL.ERROR_NO_JAVACURL_HANDLE_AVAILABLE);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * CurlGlue gateway to the <code>CURLException</code> and 
	 * <code>CURLError</code> infrastructure
	 * @param code the CURL.ERROR_* that has been thrown
	 * @see CURL CURL
	 * @see CURLException CURLException
	 * @see CURLError CURLError
	 */
	private static void error(int code) {
		CURL.error(code);
	}
	
	// currently does nothing
	public native int getinfo();

	/**
	 * This method must be called after setting all your options. The minimum
	 * requirement by libcurl is to set the <code>CURL.OPT_URL</code> option. 
	 * @return result of the operation
	 */
	public int perform() {
		// TODO: Need to check for fatal libcurl errors before passing
		// result code back to user
		return jni_perform(javacurl_handle);
	}
	
	public int formadd(int form_opt_copyname, String name,
					   int form_opt_file, String file,
					   int form_opt_type, String type) {
		return jni_formadd(javacurl_handle, form_opt_copyname, name, 
				form_opt_file, file, form_opt_type, type);
	}
	
	public int formfree() {
		return jni_formfree(javacurl_handle);
	}

	/**
	 * Tells curl how to behave by setting a curl option. Handles all long 
	 * and boolean(0,1) curl options.
	 * @param option CURL.OPT_* 
	 * @param value Non-zero informs libcurl to do something, zero turns option off.
	 * @return result of operation
	 * @exception CURLException <ul>
	 * 		<li>ERROR_BAD_ARGUMENT - argument passed is outside the range
	 * 			of valid options</li></ul>
	 */
	public int setopt(int option, int value) {
		// At the moment we have 1 through 128 options defined
		if (value < 0 || value > 128)
			error(CURL.ERROR_BAD_ARGUMENT);
		return jni_setopt(javacurl_handle, option, value);
	}
	
	/**
	 * Tells curl how to behave by setting a curl option. This method is 
	 * used to pass along string objects, such as a URL, to libcurl
	 * @param option CURL.OPT_*
	 * @param value String
	 * @return result of operation
	 * @exception CURLException <ul>
	 * 		<li>ERROR_NULL_ARGUMENT - a null string has been passed</li></ul>
	 */
	public int setopt(int option, String value) {
		if (value == null)
			error(CURL.ERROR_NULL_ARGUMENT);
		return jni_setopt(javacurl_handle, option, value);
	}

	/**
	 * Sets an external callback function for curl to pass data to. 
	 * @param option CURL.OPT_*FUNCTION
	 * @param value CurlWrite callback function
	 * @return result of operation
	 * @exception CURLException <ul>
	 * 		<li>ERROR_NO_CALLBACK_INSTANCE - the callback function has
	 * 			not been instantiated (initialized)</li></ul>
	 */
	public int setopt(int option, CurlWrite value) {
		if ((value instanceof CurlWrite) != true)
			error(CURL.ERROR_NO_CALLBACK_INSTANCE);
		return jni_setopt(javacurl_handle, option, value);
	}

	/**
	 * This must be called for every instance of the class. Performs
	 * essential libcurl cleanup and releases resources.
	 */
	public void finalize() {
		jni_cleanup(javacurl_handle);
	}

	/**
	 * Instantiate this class and load the dependent libraries. JVM ignores 
	 * the system call if the library has been pre-loaded by other means.
	 */
	static {
		try {
			// This is potentially nasty... if a user of javacurl
			// does not have openssl or msvcr70(? inlcluded with WinXP?)
			// then javacurl will not be loaded.
			// *** Temporary solution for SSL enabled javacurl ***
			// *** SSL support should be implemented as a "Plug-in Fragment" ***
			System.loadLibrary("msvcr70");
			System.loadLibrary("libeay32");
			System.loadLibrary("ssleay32");
			System.loadLibrary("javacurl");
		} catch (SecurityException se) {
			error(CURL.ERROR_SECURITY_MANAGER_VIOLATION);
		} catch (UnsatisfiedLinkError ule) {
			error(CURL.ERROR_FAILED_LOAD_LIBRARY);
		} 
		
		System.out.println("Libraries loaded.");
	}
}